<!DOCTYPE html>


<html lang="en">
  

    <head>
      <meta charset="utf-8" />
        
      <meta
        name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1"
      />
      <title>《JavaScript高级程序设计》- 第十二章：BOM |  欢迎</title>
  <meta name="generator" content="hexo-theme-ayer">
      
      <link rel="shortcut icon" href="/favicon.ico" />
       
<link rel="stylesheet" href="/dist/main.css">

      <link
        rel="stylesheet"
        href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css"
      />
      
<link rel="stylesheet" href="/css/custom.css">
 
      <script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>
       
 

      <link
        rel="stylesheet"
        href="https://cdn.jsdelivr.net/npm/@sweetalert2/theme-bulma@5.0.1/bulma.min.css"
      />
      <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.0.19/dist/sweetalert2.min.js"></script>

      <!-- mermaid -->
      
      <style>
        .swal2-styled.swal2-confirm {
          font-size: 1.6rem;
        }
      </style>
    <link rel="alternate" href="/atom.xml" title="欢迎" type="application/atom+xml">
</head>
  </html>
</html>


<body>
  <div id="app">
    
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-JavaScript学习/《JavaScript高级程序设计》- 第十二章：BOM"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  《JavaScript高级程序设计》- 第十二章：BOM
</h1>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2022/02/26/JavaScript%E5%AD%A6%E4%B9%A0/%E3%80%8AJavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E3%80%8B-%20%E7%AC%AC%E5%8D%81%E4%BA%8C%E7%AB%A0%EF%BC%9ABOM/" class="article-date">
  <time datetime="2022-02-26T10:48:24.000Z" itemprop="datePublished">2022-02-26</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/JavaScript%E5%AD%A6%E4%B9%A0/">JavaScript学习</a>
  </div>
  
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> Word count:</span>
            <span class="post-count">4.1k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> Reading time≈</span>
            <span class="post-count">16 min</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <h2 id="第十二章：BOM"><a href="#第十二章：BOM" class="headerlink" title="第十二章：BOM"></a>第十二章：BOM</h2><p>BOM（Brower Object Model）：浏览器对象模型；</p>
<p>提供了与网页无关的浏览器功能对象</p>
<h3 id="12-1-window对象"><a href="#12-1-window对象" class="headerlink" title="12.1 window对象"></a>12.1 window对象</h3><p><code>BOM</code>的<strong>核心</strong>是<code>window</code>对象，表示浏览器实例；</p>
<p><code>window</code>对象有两重身份</p>
<ul>
<li>ECMASCript中的Global对象</li>
<li>浏览器窗口的JavaScript接口</li>
</ul>
<p>这就意味着：网页中所有定义的对象、变量和函数都是以window作为其Global对象。</p>
<blockquote>
<p>注意：window对象的属性，再不同的浏览器中，<strong>差异可能会很大</strong>；</p>
</blockquote>
<h4 id="12-1-1-Global作用域"><a href="#12-1-1-Global作用域" class="headerlink" title="12.1.1 Global作用域"></a>12.1.1 Global作用域</h4><p>由于<code>window</code>被复用为<code>Global</code>的对象，所以<code>var</code>声明的全局变量、函数，都会称为<code>window</code>的属性、方法；</p>
<h4 id="12-1-2-窗口关系"><a href="#12-1-2-窗口关系" class="headerlink" title="12.1.2 窗口关系"></a>12.1.2 窗口关系</h4><ol>
<li>top对象：始终指向最上层窗口，即浏览器窗口本身</li>
<li>parent对象：指向当前窗口的父窗口</li>
<li>self对象：终极window属性，始终指向window</li>
</ol>
<h4 id="12-1-3-窗口位置与像素比"><a href="#12-1-3-窗口位置与像素比" class="headerlink" title="12.1.3 窗口位置与像素比"></a>12.1.3 窗口位置与像素比</h4><blockquote>
<p>窗口位置，可以通过如下函数来获取、调节：</p>
</blockquote>
<ul>
<li>screenLeft：获取浏览器<strong>相对于屏幕左侧</strong>的位置，返回单位是<strong>像素</strong></li>
<li>screenTop：获取浏览器<strong>相对于屏幕顶部</strong>的位置，返回单位是<strong>像素</strong></li>
<li>moveTo()：移动到新的（x，y）上，接收两个参数：<ul>
<li>x：屏幕上的绝对坐标</li>
<li>y：屏幕上的绝对坐标</li>
</ul>
</li>
<li>moveBy()：将窗口偏移（xoffset，yoffset），接收两个参数：<ul>
<li>xoffset：负数表示左偏移，正数反之</li>
<li>yoffset：负数表示上偏移，正数反之</li>
</ul>
</li>
</ul>
<blockquote>
<p>像素比：物理分辨率 ÷ 逻辑分辨率</p>
</blockquote>
<p>可以通过<code>window.devicePixeRatio</code>获取像素比</p>
<h4 id="12-1-4-窗口大小"><a href="#12-1-4-窗口大小" class="headerlink" title="12.1.4 窗口大小"></a>12.1.4 窗口大小</h4><blockquote>
<p>获取浏览器<strong>窗口</strong>大小</p>
</blockquote>
<ul>
<li>outerWidth</li>
<li>outerHeight</li>
</ul>
<blockquote>
<p>获取浏览器窗口中<strong>页面视口</strong>大小（不包括浏览器边框和工具栏）</p>
</blockquote>
<ul>
<li>innerWidth</li>
<li>innerHeight</li>
</ul>
<blockquote>
<p>通过DOM的形式获取布局页面视口的大小</p>
</blockquote>
<ul>
<li>document.documentElement.clientWidth</li>
<li>document.documentElement.clientHeight</li>
</ul>
<blockquote>
<p>浏览器窗口自身大小精确尺寸不好缺点，但是可以精确确定视口大小</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> pageWidth = <span class="built_in">window</span>.innerWidth,</span><br><span class="line">    pageHeight = <span class="built_in">window</span>.innerHeight;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">typeof</span> pageWidth != <span class="string">&#x27;number&#x27;</span> || <span class="keyword">typeof</span> pageHeight != <span class="string">&#x27;number&#x27;</span>)&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span>( <span class="built_in">document</span>.compatMode == <span class="string">&#x27;CSS1Compat&#x27;</span> ) &#123;</span><br><span class="line">        pageWidth = <span class="built_in">document</span>.documentElement.clientWidth;</span><br><span class="line">        pageHeight = <span class="built_in">document</span>.documentElement.clientHeight;</span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        pageWidth = <span class="built_in">document</span>.body.clientWidth;</span><br><span class="line">        pageHeight = <span class="built_in">document</span>.body.clientHeight;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<blockquote>
<p>由于移动端与桌面浏览器的差异化，所以需要先确定用户是否使用移动设备；</p>
</blockquote>
<h4 id="12-1-5-视口位置"><a href="#12-1-5-视口位置" class="headerlink" title="12.1.5 视口位置"></a>12.1.5 视口位置</h4><blockquote>
<p>可以使用以下三种方法<strong>滚动页面</strong>:</p>
</blockquote>
<ul>
<li>scroll()：滚动窗口至文档中的特定位置。</li>
<li>scrollTo()：将视口移动到（x，y）上，接收两个参数：<ul>
<li>x：视口上的绝对坐标</li>
<li>y：视口上的绝对坐标</li>
</ul>
</li>
<li>scrollBy()：将视口偏移（xoffset，yoffset），接收两个参数：<ul>
<li>xoffset：负数表示左偏移，正数反之</li>
<li>yoffset：负数表示上偏移，正数反之</li>
</ul>
</li>
</ul>
<blockquote>
<p>与此同时，以上三个方法都可以接收一个<strong>字典</strong>，即对象；</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 正常滚动</span></span><br><span class="line"><span class="built_in">window</span>.scrollTo(&#123;</span><br><span class="line">    <span class="attr">left</span>:<span class="number">100</span>,</span><br><span class="line">    <span class="attr">top</span>:<span class="number">100</span>,</span><br><span class="line">    <span class="attr">behavior</span>:<span class="string">&#x27;auto&#x27;</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 平滑滚动</span></span><br><span class="line"><span class="built_in">window</span>.scrollTo(&#123;</span><br><span class="line">    <span class="attr">left</span>:<span class="number">100</span>,</span><br><span class="line">    <span class="attr">top</span>:<span class="number">100</span>,</span><br><span class="line">    <span class="attr">behavior</span>:<span class="string">&#x27;smooth&#x27;</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>



<h4 id="12-1-6-导航与打开新的窗口"><a href="#12-1-6-导航与打开新的窗口" class="headerlink" title="12.1.6 导航与打开新的窗口"></a>12.1.6 导航与打开新的窗口</h4><blockquote>
<p>可以使用<code>window.open()</code>方法用于导航至指定URL或者打开新的浏览器窗口。其<strong>返回一个窗口的引用</strong>；其接收4个参数：</p>
</blockquote>
<ul>
<li><p>加载的URL：</p>
</li>
<li><p>目标窗口：接收一个窗口名字，如果该名字不存在，则以该名<strong>命名并创建一个新的窗口</strong></p>
</li>
<li><p>特性字符串：</p>
<table>
<thead>
<tr>
<th>设置</th>
<th>值</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>fullscreen</td>
<td>“yes”或”no”</td>
<td>表示新窗口是否最大化。  仅限IE支持</td>
</tr>
<tr>
<td>height</td>
<td>数值</td>
<td>新窗口高度。  这个值不能小于100</td>
</tr>
<tr>
<td>left</td>
<td>数值</td>
<td>新窗口的x轴坐标。 这个值不能是负值</td>
</tr>
<tr>
<td>location</td>
<td>“yes”或”no”</td>
<td>表示是否显示地址栏。  不同浏览器的默认值也不一样。 在     设置为”no”时，  地址栏可能隐藏或禁用（取决于浏览器）</td>
</tr>
<tr>
<td>Menubar</td>
<td>“yes”或”no”</td>
<td>表示是否显示菜单栏。  默认为”no”</td>
</tr>
<tr>
<td>resizable</td>
<td>“yes”或”no”</td>
<td>表示是否可以拖动改变新窗口大小。  默认为”no”</td>
</tr>
<tr>
<td>scrollbars</td>
<td>“yes”或”no”</td>
<td>表示是否可以在内容过长时滚动。  默认为”no”</td>
</tr>
<tr>
<td>status</td>
<td>“yes”或”no”</td>
<td>表示是否显示状态栏。  不同浏览器的默认值也不一样</td>
</tr>
<tr>
<td>toolbar</td>
<td>“yes”或”no”</td>
<td>表示是否显示工具栏。  默认为”no”</td>
</tr>
<tr>
<td>top</td>
<td>数值</td>
<td>新窗口的y轴坐标。 这个值不能是负值</td>
</tr>
<tr>
<td>width</td>
<td>数值</td>
<td>新窗口的宽度。  这个值不能小于100</td>
</tr>
</tbody></table>
</li>
<li><p>布尔值：表示新窗口在浏览器记录中是否代替当前加载页面</p>
</li>
</ul>
<blockquote>
<p>close()可以关闭窗口，closed属性可以检查窗口是否关闭</p>
</blockquote>
<blockquote>
<p>opener属性，指向打开新窗口的窗口；如果需要将两者的关系分离，可以设置<code>newWindow.opener = null</code>，一旦切断链接，就无法恢复；</p>
</blockquote>
<blockquote>
<p>安全限制：</p>
</blockquote>
<p>因为弹窗被滥用于现在广告，所以浏览器对弹窗施加了诸多限制：</p>
<ul>
<li>不允许创建屏幕之外的弹窗</li>
<li>禁止隐藏状态栏</li>
<li>在用户操作下才允许弹窗</li>
</ul>
<blockquote>
<p>弹窗屏蔽程序</p>
</blockquote>
<p>由于弹窗有诸多限制，所以我们可以在使用之前，进行检测：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> blocked = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> bdWin = <span class="built_in">window</span>.open(<span class="string">&#x27;https://www.baidu.com&#x27;</span>,<span class="string">&#x27;bdWin&#x27;</span>);</span><br><span class="line">    <span class="keyword">if</span>(bdWin == <span class="literal">null</span>)&#123;</span><br><span class="line">        blocked = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line">    blocked = <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( blocked )&#123;</span><br><span class="line">    alert(<span class="string">&quot;弹窗已经被禁止！&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h4 id="12-1-7-定时器"><a href="#12-1-7-定时器" class="headerlink" title="12.1.7 定时器"></a>12.1.7 定时器</h4><blockquote>
<p>延迟定时器<code>setTimeout()</code>，接收两个参数：</p>
</blockquote>
<ul>
<li>执行函数</li>
<li>延迟时间（单位毫秒）</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">setTimeouot( <span class="function">()=&gt;</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;success&#x27;</span>);</span><br><span class="line">&#125;,<span class="number">1000</span>);		<span class="comment">// 大概一秒后输出：success</span></span><br></pre></td></tr></table></figure>



<p>其原理大概为：JavaScript维护了一个<strong>任务队列</strong>，其中的<strong>任务按顺序执行</strong>；<code>setTimeout()</code>的第二个参数告诉JavaScript引擎，<strong>N毫秒后</strong>，<strong>将任务推入任务队列中</strong>，<strong>等待执行</strong>；如果任务队列中没有其他任务，就会立即执行；反之，需要等待。</p>
<blockquote>
<p>延迟定时器，会<strong>返回一个超时排期的数值ID</strong>，可以用于<strong>清除该未执行的延迟定时器</strong>；</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> timeoutId = <span class="built_in">setTimeout</span>( <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;success&#x27;</span>);</span><br><span class="line">&#125;,<span class="number">10000</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">clearTimeout</span>(timeoutId);	<span class="comment">// 由于十秒后才会推入任务队列，所以现在可以清除</span></span><br></pre></td></tr></table></figure>



<blockquote>
<p>循环定时器<code>setInterval()</code>，与延迟定时器类似，也是接收两个参数；</p>
</blockquote>
<p>其原理大概为：每个N毫秒后，向任务队列推入新的任务，等待执行；除非取消循环定时器或页面卸载，不然将会一致循环下去；</p>
<p>由此可见，循环定时器适合<strong>执行时间短、非阻塞的回调函数</strong></p>
<blockquote>
<p>循环定时器常见的例子</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> num = <span class="number">0</span>, intervalId = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">let</span> max = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> incrementNumber = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">	num++;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 如果到达最大值，则取消所有未执行的任务</span></span><br><span class="line">    <span class="keyword">if</span>(num == max)&#123;</span><br><span class="line">        <span class="built_in">clearInterval</span>(intervalId);</span><br><span class="line">        alert(<span class="string">&#x27;Done&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">intervalId = <span class="built_in">setInterval</span>(intervalId);</span><br></pre></td></tr></table></figure>



<blockquote>
<p>一般很少会使用<code>setInterval()</code>来实现，推荐使用<code>setTimeout()</code>来实现；</p>
<p>因为使用后者，不需要记录ID，就可以自动停止；</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> num = <span class="number">0</span>, timeoutId = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">let</span> max = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> incrementNumber = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">	num++;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 如果到达最大值，则取消所有未执行的任务</span></span><br><span class="line">    <span class="keyword">if</span>(num &lt; max)&#123;</span><br><span class="line">        <span class="built_in">setTimeout</span>(incrementNumber,<span class="number">500</span>)</span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        alert(<span class="string">&#x27;Done&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">setTimeout</span>(incrementNumber,<span class="number">500</span>);</span><br></pre></td></tr></table></figure>



<h4 id="12-1-8-系统对话框"><a href="#12-1-8-系统对话框" class="headerlink" title="12.1.8 系统对话框"></a>12.1.8 系统对话框</h4><blockquote>
<p>同步的对话框：需要进行<strong>操作后</strong>，<strong>才能返回页面继续操作</strong></p>
</blockquote>
<ul>
<li><p>alert()：接收一个参数：<strong>输出的警告内容</strong>；无返回值；</p>
</li>
<li><p>confirm()：接收一个参数：<strong>输出的内容</strong>；具有返回值：</p>
<ul>
<li>true：代表点击了确定</li>
<li>false：代表关闭对话框或者点击了取消</li>
</ul>
</li>
<li><p>prompt()：接收<strong>两个参数</strong>：</p>
<ul>
<li>提示的文本</li>
<li>输入的文本</li>
</ul>
<p><strong>有返回值</strong>：</p>
<ul>
<li>返回输入的文本</li>
</ul>
</li>
</ul>
<blockquote>
<p>很多浏览器针在存在<strong>两个或两个以上的系统对话框时</strong>，在<strong>第二个系统对话框会提供复选框</strong>，用于<strong>是否禁止继续弹出系统对话框</strong>；</p>
</blockquote>
<blockquote>
<p>异步的对话框：<strong>不需要关闭对话框</strong>，也<strong>可以继续操作页面</strong>；【使用场景很少】</p>
</blockquote>
<ul>
<li>find()：相当于浏览器中的查找功能实现的地方；</li>
<li>print()：相当于浏览器中的打印功能实现的地方。</li>
</ul>
<h3 id="12-2-location对象"><a href="#12-2-location对象" class="headerlink" title="12.2 location对象"></a>12.2 location对象</h3><p><code>location</code>对象：</p>
<ul>
<li>提供了当前窗口中加载文档的信息；</li>
<li>以及通常的导航功能；</li>
<li>它既是<code>window</code>属性、也是<code>document</code>属性！</li>
</ul>
<h4 id="12-2-1-location-与-URL"><a href="#12-2-1-location-与-URL" class="headerlink" title="12.2.1 location 与 URL"></a>12.2.1 <code>location</code> 与 <code>URL</code></h4><p>假设当前加载的URL为：<code>http://foouser:barpassword@www.wrox.com:80/WileyCDA/?q=javascript#contents</code>  </p>
<table>
<thead>
<tr>
<th>属性</th>
<th>值</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>location.hash</td>
<td>“#contents”</td>
<td>URL散列值（井号后跟零或多个字     符） ， 如果没有则为空字符串</td>
</tr>
<tr>
<td>location.host</td>
<td>“<a href="http://www.wrox.com:80&quot;">www.wrox.com:80&quot;</a></td>
<td>服务器名及端口号</td>
</tr>
<tr>
<td>location.hostname</td>
<td>“<a target="_blank" rel="noopener" href="http://www.wrox.com&quot;/">www.wrox.com&quot;</a></td>
<td>服务器名</td>
</tr>
<tr>
<td>location.href</td>
<td>“<a target="_blank" rel="noopener" href="http://www.wrox.com/WileyCDA/">http://www.wrox.com:80/WileyCDA/</a>?     q=javascript#contents”</td>
<td>当前加载页面的完整URL。 location的     toString()方法返回这个值</td>
</tr>
<tr>
<td>location.pathname</td>
<td>“/WileyCDA/“</td>
<td>URL中的路径和（或） 文件名</td>
</tr>
<tr>
<td>location.port</td>
<td>“80”</td>
<td>请求的端口。  如果URL中没有端口， 则     返回空字符串</td>
</tr>
<tr>
<td>location.protocol</td>
<td>“http:”</td>
<td>页面使用的协议。  通常     是”http:”或”https:”</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>location.search</td>
<td>“?q=javascript”</td>
<td>URL的查询字符串。 这个字符串以问号     开头</td>
</tr>
<tr>
<td>location.username</td>
<td>“foouser”</td>
<td>域名前指定的用户名</td>
</tr>
<tr>
<td>location.password</td>
<td>“barpassword”</td>
<td>域名前指定的密码</td>
</tr>
<tr>
<td>location.origin</td>
<td>“<a target="_blank" rel="noopener" href="http://www.wrox.com&quot;/">http://www.wrox.com&quot;</a></td>
<td>URL的源地址。 只读</td>
</tr>
</tbody></table>
<h4 id="12-2-2-查询字符串"><a href="#12-2-2-查询字符串" class="headerlink" title="12.2.2 查询字符串"></a>12.2.2 查询字符串</h4><p>虽然可以通过<code>location.search</code>获取需要查询的字符串，但是无法逐个访问参数，所以需要自己写一个函数去解析!</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> search = <span class="string">&quot;?id=1&amp;name=zyzc&amp;age=21&quot;</span>;</span><br><span class="line"><span class="keyword">let</span> getQueryStringArgs = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 取得没有开头问号的查询字符串</span></span><br><span class="line">    <span class="keyword">let</span> qs = (search.length &gt; <span class="number">0</span> ? search.substring(<span class="number">1</span>) : <span class="string">&quot;&quot;</span>),</span><br><span class="line">        <span class="comment">// 保存数据的对象</span></span><br><span class="line">        args = &#123;&#125;;</span><br><span class="line">        </span><br><span class="line">    <span class="comment">// 把每个参数添加到args对象</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> item <span class="keyword">of</span> qs.split(<span class="string">&quot;&amp;&quot;</span>).map(<span class="function"><span class="params">kv</span> =&gt;</span> kv.split(<span class="string">&quot;=&quot;</span>))) &#123;</span><br><span class="line">        <span class="keyword">let</span> name = <span class="built_in">decodeURIComponent</span>(item[<span class="number">0</span>]),</span><br><span class="line">            value = <span class="built_in">decodeURIComponent</span>(item[<span class="number">1</span>]);</span><br><span class="line">        <span class="keyword">if</span> (name.length) &#123;</span><br><span class="line">            args[name] = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> args;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> args = getQueryStringArgs();</span><br><span class="line"><span class="built_in">console</span>.log(args);	<span class="comment">// &#123; id: &#x27;1&#x27;, name: &quot;zyzc&quot;, age: &#x27;21&#x27; &#125;</span></span><br></pre></td></tr></table></figure>



<p>当然，除了手写以外，<code>URLSearchParams</code>还提供了一组标准API方法，通过它们可以检查和修改查询字符串；</p>
<blockquote>
<p>使用方法：给<code>URLSearchParams</code>构造函数<strong>传入查询字符串</strong>，就可以<strong>获取一个实例对象</strong>，包含<code>get</code>、<code>set</code>、<code>delete</code>等方法</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> qs = <span class="string">&quot;?q=javascript&amp;num=10&quot;</span>;</span><br><span class="line"><span class="keyword">let</span> searchParams = <span class="keyword">new</span> URLSearchParams(qs);</span><br><span class="line">alert(searchParams.toString()); <span class="comment">// &quot; q=javascript&amp;num</span></span><br><span class="line">searchParams.has(<span class="string">&quot;num&quot;</span>); <span class="comment">// true</span></span><br><span class="line">searchParams.get(<span class="string">&quot;num&quot;</span>); <span class="comment">// 10</span></span><br><span class="line">searchParams.set(<span class="string">&quot;page&quot;</span>, <span class="string">&quot;3&quot;</span>);</span><br><span class="line">alert(searchParams.toString()); <span class="comment">// &quot; q=javascript&amp;num</span></span><br><span class="line">searchParams.delete(<span class="string">&quot;q&quot;</span>);</span><br><span class="line">alert(searchParams.toString()); <span class="comment">// &quot; num=10&amp;page=3&quot;</span></span><br></pre></td></tr></table></figure>



<h4 id="12-2-3-操作地址"><a href="#12-2-3-操作地址" class="headerlink" title="12.2.3 操作地址"></a>12.2.3 操作地址</h4><p>用来操作地址最常见就是使用<code>assign()</code>传入一个<code>URL</code>，会立即导航到URL并在浏览器历史记录中添加一条记录；</p>
<p>如果换成<code>location.href</code> 或 <code>window.location</code> 设置<code>URL</code>，本质上也是调用<code>assgin()</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 三者等效</span></span><br><span class="line">location.assgin(<span class="string">&#x27;https://www.baidu.com&#x27;</span>);</span><br><span class="line">location.href = <span class="string">&#x27;https://www.baidu.com&#x27;</span>;</span><br><span class="line"><span class="built_in">window</span>.location = <span class="string">&#x27;https://www.baidu.com&#x27;</span>;</span><br></pre></td></tr></table></figure>



<p>当然，<code>location</code>对象中的属性都是可以直接赋值操作；</p>
<p>并且只要修改<code>location</code>的一个属性，页面就会<strong>重新加载新的URL</strong>（<strong>hash属性除外</strong>）；</p>
<blockquote>
<p>前面提到，修改URL会在历史记录中添加一条记录，当用户点击退后，就会返回上一个页面；</p>
<p>如果不希望增加历史记录，可以使用<code>replace()</code>方法。</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>( <span class="function">() =&gt;</span> &#123; location.replace(<span class="string">&quot;https://www.baidu.com&quot;</span>) &#125;);</span><br></pre></td></tr></table></figure>



<blockquote>
<p>还有一个类似于修改地址的方法<code>reload()</code>：它只能<strong>重新加载</strong>当前显示的页面</p>
</blockquote>
<ul>
<li>如果不传参数，页面将会以<strong>最有效的方式重新加载</strong>（可能会使用缓存）</li>
<li>如果传参<code>true</code>，页面将会<strong>从服务器中重新加载</strong></li>
</ul>
<p><strong>注意：<code>reload()</code>后面的代码，很有可能不会再执行了，所以应当将<code>reload()</code>放在最后；</strong></p>
<h3 id="12-3-navigator对象"><a href="#12-3-navigator对象" class="headerlink" title="12.3 navigator对象"></a>12.3 navigator对象</h3><p><code>navigator</code>对象，是客户端<strong>标识浏览器</strong>的标准，只要浏览器启用了<code>JavaScript</code>就会有<code>navigator</code>对象；</p>
<blockquote>
<p>列举一下<code>navigator</code>对象定义的属性和方法：</p>
</blockquote>
<table>
<thead>
<tr>
<th>属性/方法</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>activeVrDisplays</td>
<td>返回数组，  包含ispresenting属性为true的VRDisplay实例</td>
</tr>
<tr>
<td>appCodeName</td>
<td>即使在非Mozilla浏览器中也会返回”Mozilla”</td>
</tr>
<tr>
<td>appName</td>
<td>浏览器全名</td>
</tr>
<tr>
<td>appVersion</td>
<td>浏览器版本。  通常与实际的浏览器版本不一致</td>
</tr>
<tr>
<td>battery</td>
<td>返回暴露Battery Status API的BatteryManager对象</td>
</tr>
<tr>
<td>buildId</td>
<td>浏览器的构建编号</td>
</tr>
<tr>
<td>connection</td>
<td>返回暴露Network Information API的NetworkInformation对象</td>
</tr>
<tr>
<td>cookieEnabled</td>
<td>返回布尔值， 表示是否启用了cookie</td>
</tr>
<tr>
<td>credentials</td>
<td>返回暴露Credentials Management API的CredentialsContainer对     象</td>
</tr>
<tr>
<td>deviceMemory</td>
<td>返回单位为GB的设备内存容量</td>
</tr>
<tr>
<td>doNotTrack</td>
<td>返回用户的“不跟踪”（do-not-track） 设置</td>
</tr>
<tr>
<td>geolocation</td>
<td>返回暴露Geolocation API的Geolocation对象</td>
</tr>
<tr>
<td>getVRDisplays()</td>
<td>返回数组，  包含可用的每个VRDisplay实例</td>
</tr>
<tr>
<td>getUserMedia()</td>
<td>返回与可用媒体设备硬件关联的流</td>
</tr>
<tr>
<td>hardwareConcurrency</td>
<td>返回设备的处理器核心数量</td>
</tr>
<tr>
<td>javaEnabled</td>
<td>返回布尔值，  表示浏览器是否启用了Java</td>
</tr>
<tr>
<td>language</td>
<td>返回浏览器的主语言</td>
</tr>
<tr>
<td>languages</td>
<td>返回浏览器偏好的语言数组</td>
</tr>
<tr>
<td>locks</td>
<td>返回暴露Web Locks API的LockManager对象</td>
</tr>
<tr>
<td>mediaCapabilities</td>
<td>返回暴露Media Capabilities API的MediaCapabilities对象</td>
</tr>
<tr>
<td>mediaDevices</td>
<td>返回可用的媒体设备</td>
</tr>
<tr>
<td>maxTouchPoints</td>
<td>返回设备触摸屏支持的最大触点</td>
</tr>
<tr>
<td>mimeTypes</td>
<td>返回浏览器中注册的MIME类型数组</td>
</tr>
<tr>
<td>onLine</td>
<td>返回布尔值，  表示浏览器是否联网</td>
</tr>
<tr>
<td>oscpu</td>
<td>返回浏览器运行设备的操作系统和（或）  CPU</td>
</tr>
<tr>
<td>permissions</td>
<td>返回暴露Permissions API的Permissions对象</td>
</tr>
<tr>
<td>platform</td>
<td>返回浏览器运行的系统平台</td>
</tr>
<tr>
<td>plugins</td>
<td>返回浏览器安装的插件数组。  在IE中， 这个数组包含页面     中所有<code>&lt;embed&gt;</code>元素</td>
</tr>
<tr>
<td>product</td>
<td>返回产品名称（通常是”Gecko”）</td>
</tr>
<tr>
<td>productSub</td>
<td>返回产品的额外信息（通常是Gecko的版本）</td>
</tr>
<tr>
<td>registerProtocolHandler()</td>
<td>将一个网站注册为特定协议的处理程序</td>
</tr>
<tr>
<td>requestMediaKeySystemAccess()</td>
<td>返回一个期约，  解决为MediaKeySystemAccess对象</td>
</tr>
<tr>
<td>sendBeacon()</td>
<td>异步传输一些小数据</td>
</tr>
<tr>
<td>serviceWorker</td>
<td>返回用来与ServiceWorker实例交互的ServiceWorkerContainer</td>
</tr>
<tr>
<td>share()</td>
<td>返回当前平台的原生共享机制</td>
</tr>
<tr>
<td>storage</td>
<td>返回暴露Storage API的StorageManager对象</td>
</tr>
<tr>
<td>userAgent</td>
<td>返回浏览器的用户代理字符串</td>
</tr>
<tr>
<td>vendor</td>
<td>返回浏览器的厂商名称</td>
</tr>
<tr>
<td>vendorSub</td>
<td>返回浏览器厂商的更多信息</td>
</tr>
<tr>
<td>vibrate()</td>
<td>触发设备振动</td>
</tr>
<tr>
<td>webdriver</td>
<td>返回浏览器当前是否被自动化程序控制</td>
</tr>
</tbody></table>
<p><strong>navigator对象通常用于确定浏览器的类型</strong></p>
<h4 id="12-3-1-检测插件"><a href="#12-3-1-检测插件" class="headerlink" title="12.3.1 检测插件"></a>12.3.1 检测插件</h4><p>检测浏览器是否安装某个插件，是开发中常见的需求，除了IE10及更低版本的浏览器，其余都可以通过<code>plugins</code>数组来确定。</p>
<p><code>plugins</code>数组的每一项都包含如下属性：</p>
<ul>
<li><code>name</code>：插件名称</li>
<li><code>description</code>： 插件介绍。</li>
<li><code>filename</code>： 插件的文件名。</li>
<li><code>length</code>： 由当前插件处理的MIME类型数量。  </li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 插件检测， IE10及更低版本无效</span></span><br><span class="line"><span class="keyword">let</span> hasPlugin = <span class="function"><span class="keyword">function</span> (<span class="params">name</span>) </span>&#123;</span><br><span class="line">    name = name.toLowerCase();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> plugin <span class="keyword">of</span> <span class="built_in">window</span>.navigator.plugins) &#123;</span><br><span class="line">        <span class="keyword">if</span> (plugin.name.toLowerCase().indexOf(name) &gt; -<span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//检测Flash</span></span><br><span class="line">alert(hasPlugin(<span class="string">&quot;Flash&quot;</span>));</span><br><span class="line"><span class="comment">// 检测QuickTime</span></span><br><span class="line">alert(hasPlugin(<span class="string">&quot;QuickTime&quot;</span>));</span><br></pre></td></tr></table></figure>

<p>如果需要检测IE10以下的浏览器，需要这样写：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">hasIEPlugin</span>(<span class="params">name</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">try</span>&#123;</span><br><span class="line">        <span class="keyword">new</span> ActiveXObject(name);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;<span class="keyword">catch</span>(e)&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h3 id="12-4-screen对象"><a href="#12-4-screen对象" class="headerlink" title="12.4 screen对象"></a>12.4 screen对象</h3><p>这个对象使用比较少：</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>availHeight</td>
<td>屏幕像素高度减去系统组件高度（只读）</td>
</tr>
<tr>
<td>availLeft</td>
<td>没有被系统组件占用的屏幕的最左侧像素（只读）</td>
</tr>
<tr>
<td>availTop</td>
<td>没有被系统组件占用的屏幕的最顶端像素（只读）</td>
</tr>
<tr>
<td>availWidth</td>
<td>屏幕像素宽度减去系统组件宽度（只读）</td>
</tr>
<tr>
<td>colorDepth</td>
<td>表示屏幕颜色的位数；多数系统是32（只读）</td>
</tr>
<tr>
<td>height</td>
<td>屏幕像素高度</td>
</tr>
<tr>
<td>left</td>
<td>当前屏幕左边的像素距离</td>
</tr>
<tr>
<td>pixelDepth</td>
<td>屏幕的位深（只读）</td>
</tr>
<tr>
<td>top</td>
<td>当前屏幕顶端的像素距离</td>
</tr>
<tr>
<td>width</td>
<td>屏幕像素宽度</td>
</tr>
<tr>
<td>orientation</td>
<td>返回Screen Orientation API中屏幕的朝向</td>
</tr>
</tbody></table>
<h3 id="12-5-history对象"><a href="#12-5-history对象" class="headerlink" title="12.5 history对象"></a>12.5 history对象</h3><p>history对象表示当前窗口首次使用以来用户导航历史记录，history不知道URL，但是可以进行相关操作；</p>
<ul>
<li>history.go()：在历史记录中，沿任何方向导航；<ul>
<li>负数n，后退n条；</li>
<li>正数反之；</li>
<li>字符串，表示寻找到最近与该字符串匹配的URL；</li>
</ul>
</li>
<li>history.back()：退后，相当于history.go(-1)；</li>
<li>history.forward()：向前，相当于history.go(1)；</li>
<li>history.length：返回历史记录的条目数；常用于判断是不是为第一个页面；</li>
</ul>
<h4 id="12-5-1-历史状态管理"><a href="#12-5-1-历史状态管理" class="headerlink" title="12.5.1 历史状态管理"></a>12.5.1 历史状态管理</h4><p>Web应用程序开发中，最难的环节之一：历史状态管理；</p>
<blockquote>
<p>暂时没看懂再表达什么，先放一下；</p>
</blockquote>
<h3 id="12-6-小结"><a href="#12-6-小结" class="headerlink" title="12.6 小结"></a>12.6 小结</h3><ul>
<li>window：提供了操作浏览器的可能</li>
<li>location：提供了操作地址的可能</li>
<li>navigator：提供了获取浏览器信息的可能</li>
<li>screen：提供了屏幕信息的可能</li>
<li>history：提供了操作浏览器历史的可能</li>
</ul>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <div class="declare">
      <ul class="post-copyright">
        <li>
          <i class="ri-copyright-line"></i>
          <strong>Copyright： </strong>
          
          Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
          
        </li>
      </ul>
    </div>
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=http://example.com/2022/02/26/JavaScript%E5%AD%A6%E4%B9%A0/%E3%80%8AJavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E3%80%8B-%20%E7%AC%AC%E5%8D%81%E4%BA%8C%E7%AB%A0%EF%BC%9ABOM/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/JavaScript/" rel="tag">JavaScript</a></li></ul>

    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2022/02/27/JavaScript%E5%AD%A6%E4%B9%A0/%E3%80%8AJavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E3%80%8B-%20%E7%AC%AC%E5%8D%81%E5%9B%9B%E7%AB%A0%EF%BC%9ADOM/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            《JavaScript高级程序设计》- 第十四章：DOM
          
        </div>
      </a>
    
    
      <a href="/2022/02/25/JavaScript%E5%AD%A6%E4%B9%A0/%E3%80%8AJavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E3%80%8B-%20%E7%AC%AC%E5%8D%81%E4%B8%80%E7%AB%A0%EF%BC%9A%E6%9C%9F%E7%BA%A6/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">《JavaScript高级程序设计》- 第十一章：期约</div>
      </a>
    
  </nav>

   
<!-- valine评论 -->
<div id="vcomments-box">
  <div id="vcomments"></div>
</div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/valine@1.4.14/dist/Valine.min.js"></script>
<script>
  new Valine({
    el: "#vcomments",
    app_id: "jeb2pvxjdeJQOj5OG3AUavGv-gzGzoHsz",
    app_key: "6YlB9tQJTstHMM8GOwFu4uyY",
    path: window.location.pathname,
    avatar: "monsterid",
    placeholder: "给我的文章加点评论吧~",
    recordIP: true,
  });
  const infoEle = document.querySelector("#vcomments .info");
  if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0) {
    infoEle.childNodes.forEach(function (item) {
      item.parentNode.removeChild(item);
    });
  }
</script>
<style>
  #vcomments-box {
    padding: 5px 30px;
  }

  @media screen and (max-width: 800px) {
    #vcomments-box {
      padding: 5px 0px;
    }
  }

  #vcomments-box #vcomments {
    background-color: #fff;
  }

  .v .vlist .vcard .vh {
    padding-right: 20px;
  }

  .v .vlist .vcard {
    padding-left: 10px;
  }
</style>

 
   
     
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2022
        <i class="ri-heart-fill heart_icon"></i> 子与子乘
      </li>
    </ul>
    <ul>
      <li>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>Visitors:<span id="busuanzi_value_site_uv"></span></span>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>Views:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
      </li>
    </ul>
  </div>
</footer>    
    </main>
    <div class="float_btns">
      <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

    </div>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="欢迎"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="Search">
        <i class="ri-search-line"></i>
      </a>
      
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i></p>
  <div class="reward-box">
    
    
  </div>
</div>
    
<script src="/js/jquery-3.6.0.min.js"></script>
 
<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->
 
<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: ".tocbot",
    contentSelector: ".article-entry",
    headingSelector: "h1, h2, h3, h4, h5, h6",
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: "main",
    positionFixedSelector: ".tocbot",
    positionFixedClass: "is-position-fixed",
    fixedSidebarOffset: "auto",
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->
 <!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script> 
<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->
 
<script src="/js/busuanzi-2.3.pure.min.js"></script>
 
<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->
 
<link rel="stylesheet" href="/css/clipboard.css">
 <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>
 
<!-- CanvasBackground -->

<script>
  if (window.mermaid) {
    mermaid.initialize({ theme: "forest" });
  }
</script>


    
    

  </div>
</body>

</html>